package com.jjs.jcenterdemo;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.OvershootInterpolator;

import com.white.commonlib.widget.AbsView;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Created by ytf on 2018/3/26 026.
 * Description:
 */

public class RecordRippleButton extends AbsView
{

    private Bitmap bmpNormal;
    private Paint circleBgPaint, ringLinePaint, ripplePaint;

    private int ringLineColor = 0xFFCCCCCC;
    private int themeColor = 0xFF40a6ff;

    private ValueAnimator scaleInAnim, scaleOutAnim;

    public RecordRippleButton(Context context)
    {
        this(context, null);
    }

    public RecordRippleButton(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        init();
    }

    private void init()
    {
        circleBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circleBgPaint.setStyle(Paint.Style.FILL);
        circleBgPaint.setColor(themeColor);

        ringLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        ringLinePaint.setStyle(Paint.Style.STROKE);
        ringLinePaint.setStrokeWidth(2);
        ringLinePaint.setColor(ringLineColor);

        ripplePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        ripplePaint.setStyle(Paint.Style.FILL);
        ripplePaint.setColor(themeColor);

        BitmapDrawable bdp = (BitmapDrawable) getContext().getResources().getDrawable(R.drawable.record_button_white);
        bmpNormal = bdp.getBitmap();

        prepareAnim();
    }

    private float scaleFactor = 1.0f;
    private boolean isAnimGoing;

    private void prepareAnim()
    {
        scaleInAnim = ValueAnimator.ofFloat(1.0f, 0.8f);
        scaleInAnim.setDuration(500);
        scaleInAnim.setInterpolator(new OvershootInterpolator());
        scaleInAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
        {
            @Override
            public void onAnimationUpdate(ValueAnimator animation)
            {
                scaleFactor = (float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        scaleInAnim.addListener(new Animator.AnimatorListener()
        {
            @Override
            public void onAnimationStart(Animator animation)
            {
                isAnimGoing = true;
            }

            @Override
            public void onAnimationEnd(Animator animation)
            {
                isAnimGoing = false;
            }

            @Override
            public void onAnimationCancel(Animator animation)
            {

            }

            @Override
            public void onAnimationRepeat(Animator animation)
            {

            }
        });
        scaleOutAnim = ValueAnimator.ofFloat(0.8f, 1.0f);
        scaleOutAnim.setDuration(500);
        scaleOutAnim.setInterpolator(new OvershootInterpolator());
        scaleOutAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
        {
            @Override
            public void onAnimationUpdate(ValueAnimator animation)
            {
                scaleFactor = (float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        scaleOutAnim.addListener(new Animator.AnimatorListener()
        {
            @Override
            public void onAnimationStart(Animator animation)
            {
                isAnimGoing = true;
            }

            @Override
            public void onAnimationEnd(Animator animation)
            {
                isAnimGoing = false;
            }

            @Override
            public void onAnimationCancel(Animator animation)
            {

            }

            @Override
            public void onAnimationRepeat(Animator animation)
            {

            }
        });
    }

    @Override
    public int getDefaultWrapWidth()
    {
        return 500;
    }

    @Override
    public int getDefaultWrapHeight()
    {
        return 500;
    }


    private boolean isOnRecording;

    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        if(isOnRecording)
        {
            drawRipple(canvas);
        }else
        {
            drawRings(canvas);
        }
        drawCenterIcon(canvas);
    }

    private void drawRipple(Canvas canvas)
    {
        for (Circle c : circles)
        {
            c.draw(canvas);
        }
    }

    private void drawCenterIcon(Canvas canvas)
    {
        canvas.save();
        canvas.scale(scaleFactor, scaleFactor, halfWidth, halfHeight);
        canvas.drawCircle(mBmpBgRect.centerX(), mBmpBgRect.centerY(), mBmpBgRect.width()/2, circleBgPaint);
        canvas.drawBitmap(bmpNormal, mBmpMatrix, null);
        canvas.restore();
    }

    private void drawRings(Canvas canvas)
    {
        for (int i = 0; i < rings.size(); i++)
        {
            ringLinePaint.setAlpha(rings.get(i).getAlpha());
            canvas.drawCircle(halfWidth, halfHeight, rings.get(i).radius, ringLinePaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev)
    {
        switch(ev.getActionMasked())
        {
            case MotionEvent.ACTION_DOWN:

                //呼吸波纹扩散
                scaleOutAnim.cancel();
                scaleInAnim.cancel();
                scaleInAnim.start();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_OUTSIDE:
                scaleOutAnim.cancel();
                scaleInAnim.cancel();
                scaleOutAnim.start();

                isOnRecording = !isOnRecording;

                if(isOnRecording)
                {
                    rippleStart();
                }else
                {
                    rippleStop();
                }
                break;
            default:
                break;
        }
        return true;
    }

    private Matrix mBmpMatrix = new Matrix();
    private RectF mBmpBgRect;
    private List<Ring> rings =  new ArrayList<>();

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);

        int bmpWidth = bmpNormal.getWidth();
        int bmpHeight = bmpNormal.getHeight();
        int offsetX = halfWidth - bmpWidth/2;
        int offsetY = halfHeight - bmpHeight/2;
        mBmpMatrix.reset();
        mBmpMatrix.postTranslate(offsetX, offsetY);

        int left = halfWidth-bmpWidth/2 - 30;
        int right = left + bmpWidth + 60;
        int top = halfHeight - bmpHeight/2 - 30;
        int bottom = top + bmpHeight + 60;
        mBmpBgRect = new RectF(left, top, right, bottom);

        calcRings();

        prepareCircle();
    }

    private void prepareCircle()
    {
        circles.clear();

        int init = (int) (mBmpBgRect.width()/2);

        Circle c1 = new Circle(ripplePaint, halfWidth, halfHeight, init);
        Circle c2 = new Circle(ripplePaint, halfWidth, halfHeight, init);
        Circle c3 = new Circle(ripplePaint, halfWidth, halfHeight, init);
        Circle c4 = new Circle(ripplePaint, halfWidth, halfHeight, init);
        Circle c5 = new Circle(ripplePaint, halfWidth, halfHeight, init);
        circles.add(c1);
        circles.add(c2);
        circles.add(c3);
        circles.add(c4);
        circles.add(c5);

        if (thread != null)
        {
            thread.kill();
        }

        isOnRecording = false;

        thread = new ExpandThread(100, halfHeight, 3, 15, circles);
    }

    private void calcRings()
    {
        float step = (mBmpBgRect.left - mBmpBgRect.width() / 4 ) / 3.0f;
        rings.clear();
        float start = mBmpBgRect.width()/2 + step;
        for (int i = 0; i < 3; i++)
        {
            Ring r = new Ring(i, start + i * step);
            rings.add(r);
        }
    }

    private void rippleStart()
    {
        if (thread != null)
        {
            if(!thread.isOnRunning())
            {
                thread.start();
            }else
            {
                thread.cirResume();
            }
        }
    }

    private void rippleStop()
    {
        if (thread != null && thread.isOnRunning())
        {
            thread.cirPause();
        }
    }

    @Override
    protected void onDetachedFromWindow()
    {
        super.onDetachedFromWindow();
        if (thread != null)
        {
            thread.kill();
        }
        circles.clear();
        rings.clear();
    }

    @Override
    public void onGlobalLayout()
    {

    }

    class Ring
    {
        int index;
        float radius;

        public Ring(int index, float radius)
        {
            this.index = index;
            this.radius = radius;
        }

        public int getAlpha()
        {
            return (int) (120 * ((3 - index) / 3.0f));
        }
    }

    private ExpandThread thread;

    class ExpandThread extends Thread
    {
        float min, max;
        float interval;
        int delayTime;
        List<Circle> circles;
        boolean needExit;
        boolean onRunning;
        private int offsetDistance;
        private int size;
        private boolean pauseUpdate;

        public void setPauseUpdate(boolean pauseUpdate)
        {
            this.pauseUpdate = pauseUpdate;
        }

        public ExpandThread(float minRadius, float maxRadius,
                            float distanceInterval, int intervalTime, List<Circle> circles)
        {
            min = minRadius;
            max = maxRadius;
            interval = distanceInterval;
            delayTime = intervalTime;
            this.circles = circles;
            offsetDistance = (int) ((max - min) / circles.size());
            size = circles.size();
            resume = true;

        }

        public boolean isOnRunning()
        {
            return onRunning;
        }

        public void kill()
        {
            needExit = true;
        }

        private final Object obj = new Object();

        @Override
        public void run()
        {
            synchronized (obj)
            {
                while (!needExit)
                {
                    onRunning = true;
                    postInvalidate();
                    SystemClock.sleep(delayTime);

                    try
                    {
                        if(resetFlag)
                        {
                            obj.wait();
                        }
                    } catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    if(resume)
                        loopCircles();
                }
                onRunning = false;
            }
        }

        private void loopCircles()
        {
            if (circles.size() > 0)
            {
                for (int i = 0; i < size; i++)
                {
                    Circle current = circles.get(i);
                    int nextIndex = (i + 1 == size ? 0 : i + 1);
                    Circle next = circles.get(nextIndex);
                    if (i == 0 && !circles.get(size - 1).isUpdated)
                    {
                        current.update(min, max, interval);
                    } else
                    {
                        if (current.isUpdated)
                        {
                            current.update(min, max, interval);
                        }
                    }

                    //启动下一个圆的更新
                    if (current.getCurrentIncrement() >= offsetDistance)
                    {
                        if (!next.isUpdated)
                        {
                            next.update(min, max, interval);
                        }
                    }
                }
            }
        }

        private boolean resume;

        private boolean resetFlag;

        public void cirPause()
        {
            resetFlag = true;
            synchronized (obj)
            {
                for(Circle c : circles)
                    c.reset();

                resume = false;
                obj.notify();
            }
            resetFlag = false;
        }

        public void cirResume()
        {
            resume = true;
        }
    }

    private List<Circle> circles = new ArrayList<>();

    class Circle
    {
        int centerX, centerY;
        float radius;
        float initRadius;
        Paint mPaint;
        int alpha;
        boolean isUpdated;
        private String name;

        public Circle(Paint p, int cx, int cy, int initRadius)
        {
            Random rd = new Random();
            this.mPaint = p;
            centerX = cx;
            centerY = cy;
            alpha = 180;
            radius = initRadius;
            this.initRadius = initRadius;
            name = String.valueOf(rd.nextInt(100));
        }

        public void reset()
        {
            radius = initRadius;
            isUpdated = false;
            alpha = 180;
        }

        public void update(float minR, float maxR, float radiusInterval)
        {
            isUpdated = true;
            radius += radiusInterval;
            if (radius >= maxR)
            {
                radius = minR;
                isUpdated = false;
            }
            alpha = (int) (180 * (1 - radius / maxR));
        }

        public float getCurrentIncrement()
        {
            return radius - initRadius;
        }

        @Override
        public String toString()
        {
            return name;
        }

        public void draw(Canvas c)
        {
            mPaint.setAlpha(this.alpha);
            c.drawCircle(centerX, centerY, radius, this.mPaint);
        }
    }

    Callback ll;
    public void setCallback(Callback c)
    {
        ll = c;
    }

    public interface Callback
    {
        void onStart();
        void onStop();
    }
}
